import { Interpreter } from 'eval5';
import { Modal } from 'antd';
import TableList from '../../components/auditHistory/_TableList';
import dayjs from 'dayjs';
import * as XLSX from 'xlsx/xlsx.mjs';
import ExcelJS from 'exceljs';
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { units, history } from '@library';
import { moneyFormat } from '@library/components/table/util';
import { PACKAGE_NAME } from '@library/app/automatic/package';
import NP from 'number-precision';
import qs from 'query-string';
import crypto from 'crypto-js';
import convertCurrency from './convertCurrency';
import { ControlProps } from './interface';
import { ControlType } from './config';
import { flatControl, controlToTree } from './util';

const instance = axios.create({
  method: 'POST'
});

interface Response extends AxiosResponse {
  _state?: boolean;
}

interface RequestConfig extends AxiosRequestConfig {
  loading: true;
  errorMessage?: boolean;
}

type OptionType = {
  formId: string;
  formData: object;
  businessName: string;
  workflowNumber?: string;
  workflowCode?: string;
  errorMessage?: boolean;
};

interface Options {
  columns: any[];
  dataSource: any[];
  fileName?: string;
  options?: any;
}

// todoListDetail界面发出的请求需要带token，这个界面仅用于查看不用登录，额外处理
function handleTodoListDetailPageServerUrl(config: RequestConfig) {
  if (window.location.href.includes('/todoListDetail')) {
    const query = qs.parseUrl(window.location.href).query;
    const url = config.url!;

    if (!url.includes('token') && query.token) {
      config.url = `${config.url}${url.includes('?') ? '&' : '?'}token=${query.token}`;
    }
  }
}

async function server(config: RequestConfig, next: Function) {
  if (config.loading) showLoading();
  handleTodoListDetailPageServerUrl(config);
  if (next) {
    instance(config).then((result: Response) => {
      if (config.loading) hideLoading();

      // 标记业务成功
      if (result.status === 200 && result.data.status === 200) {
        result._state = true;
      }

      next(result);
    }).catch(error => {
      if (config.loading) hideLoading();

      next(error);
    });
  } else {
    return await units.serverLoading(config.url!, config.data, {
      errorMessage: config.errorMessage,
      firstMessage: true
    });
  }
}

function serverAll(configs: AxiosRequestConfig[], next: Function) {
  axios.all(
    configs.map(item => axios(item))
  ).then(result => {
    next(result);
  }).catch(error => {
    next(error);
  });
}

function showLoading() {
  window.$app.showLoading();
}


function hideLoading() {
  window.$app.hideLoading();
}

const englishLetters = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
async function exportExcel({ columns, dataSource, fileName }: Options) {
  const defaultFileName = +(new Date()) + '.xlsx';
  const workbook = new ExcelJS.Workbook();
  const worksheet = workbook.addWorksheet('Sheet1');
  const len = dataSource.length;

  worksheet.columns = columns.map(item => {
    return {
      key: item.dataIndex,
      header: item.dataIndex,
      width: item.width || 20
    }
  });

  const fiedsData = columns.reduce((next, cur) => {
    next[cur.dataIndex as string] = cur.title || window.$app.t(cur.dataIndex as string);
    return next;
  }, {});

  worksheet.addRows([fiedsData, ...dataSource]);
  columns.forEach((item, ci) => {
    if(item.dataValidation) {
      for(let i = 1; i < len + 500; i++) {
        if(i === 1) continue;
        worksheet.getCell(`${englishLetters[ci]}${i+1}`).dataValidation = item.dataValidation;
      }
    }
  })
  
  // 生成并下载Excel文件  
  workbook.xlsx.writeBuffer().then(function(buffer) {  
    var blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });  
    var link = document.createElement('a');  
    link.href = URL.createObjectURL(blob);  
    link.download = fileName || defaultFileName;  
    link.click();  
    URL.revokeObjectURL(link.href);  
  }); 
}

async function exportXlsx({ columns, dataSource, fileName, options }: Options) {
  const excelArr: Record<string, any>[] = [];

  const fiedsData = columns.reduce((next, cur) => {
    next[cur.dataIndex as string] = cur.title || window.$app.t(cur.dataIndex as string);
    return next;
  }, {});

  [fiedsData, ...dataSource].forEach((item, index) => {
    excelArr[index] = {};
    columns.forEach(column => {
      excelArr[index][column.dataIndex as string] = item[column.dataIndex as string];
    });
  });

  const defaultFileName = +(new Date()) + '.xlsx';
  const ws: XLSX.WorkSheet = XLSX.utils.json_to_sheet(excelArr, options ? options : {});
  const wsOptions = columns.map(item => ({ width: (item.width as number) || 20 }));
  ws['!cols'] = wsOptions;
  const wb: XLSX.WorkBook = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
  XLSX.writeFile(wb, fileName || defaultFileName, { cellStyles: true });
}

// 暴露个方法，为了适用于shell，在workflow lowcode无用
export function getFormUrlAndQuery(name: string) {
  return {
    url: '/bk/api/fd/Form/findOne',
    where: {
      $or: [
        { functionCode: name },
        { formName: name }
      ]
    }
  };
}

export function goToForm(name: string, state: any) {
  const pathname = history.location.pathname;
  const base = pathname.split('?')[0];
  if (state && typeof state === 'object') {
    sessionStorage.setItem('history.location.state', JSON.stringify(state));
  } else {
    sessionStorage.removeItem('history.location.state');
  }
  history.push(base + '?name=' + encodeURIComponent(name), state);
}

export function removeFormChild(structureList: ControlProps[], formName?: string) {
  const removeChildIdMap = {};

  const arr: ControlProps[] = structureList.filter(item => {
    const br = formName ? item.sourceForm !== formName : !item.sourceForm;

    if (!br && item.options?.formItems) {
      removeChildIdMap[item.options.formItems.name] = undefined;
    }

    return br;
  });

  return [arr, removeChildIdMap];
}

export function appendFormChild(formName: string, structureList: ControlProps[], preControlName: undefined | string, next: Function) {
  units.serverLoading('/bk/api/fd/Form/findOne', {
    where: { formName }
  }).then(res => {
    const content = res.content ? JSON.parse(res.content) : {};
    const list = Array.isArray(content.list) ? flatControl(content.list) : [];

    let insertList: ControlProps[] = list.filter((item: ControlProps) => {
      return item.type !== ControlType.BUTTON
        || item.type !== ControlType.SCRIPT_CHANGE
        || item.type !== ControlType.SCRIPT_INTERCEPTORS
        || item.type !== ControlType.SCRIPT_LOAD
        || item.type !== ControlType.SCRIPT_SUBMIT;
    });

    if (insertList.length) {
      const copyStructureList: ControlProps[] = [];
      const structureListMap = {};

      structureList.forEach(item => {
        copyStructureList.push(item);
        structureListMap[item.id] = true;
      });

      // 去除重复的
      insertList = insertList.filter(item => {
        if (!structureListMap[item.id]) {
          item.sourceForm = formName; // 标记来源
          return true;
        }

        return false;
      });

      if (insertList.length) {
        if (typeof preControlName !== undefined) {

          const preControlIndex = structureList.findIndex(item => {
            // 表单元素
            if (item.options?.formItemProps) {
              return item.options.formItemProps.name === preControlName;
            }

            return item.options?.name === preControlName;
          });

          copyStructureList.splice(preControlIndex + 1, 0, ...controlToTree(insertList));

          next(copyStructureList);
        } else {
          next(structureList.concat(controlToTree(insertList)));
        }
      }
    }
  });
}

const modalError = ({ title, content }: { title: string; content: string; }) => {
  Modal.error({
    title,
    content,
  });
};

// 默认注入解析器的Function
export const ctx = {
  sortByItemNo: (itemNos: string[], data: any[]) => {
    let dataObj: any = {};
    data.forEach(item => {
      dataObj[item.itemNo] = item;
    });
    return itemNos.reduce((all: any[], now) => {
      return all.concat([dataObj[now]]);
    }, []);
  },
  QS: qs,
  crypto,
  screen: window.screen,
  location: window.location,
  decodeURIComponent: window.decodeURIComponent,
  encodeURIComponent: window.encodeURIComponent,
  showHistory: (workFlowInstanceId: string) => TableList.showModal(workFlowInstanceId),
  _ENV_: 'PC',
  _BUSINESS_: {},
  NP,
  setTimeout,
  clearTimeout,
  console: {
    ...console,
    log: (window.top as any).console._log || console.log
  },
  server,
  serverAll,
  hideLoading,
  showLoading,
  goToForm,
  documentBody: document.querySelector('body'),
  documentWrite: document.write,
  historyPush: history.push,
  historyReplace: history.replace,
  error: units.message.error,
  success: units.message.success,
  _USER_: JSON.parse(sessionStorage.userInfo || '{}'),
  convertCurrency,
  moneyFormat,
  open: window.open,
  moment: dayjs,
  reload: () => window.location.reload(),
  exportXlsx: (v: object) => exportXlsx(v as any),
  confirm: ({ title, content, onOk, onCancel, okText, cancelText }: any) => {
    Modal.confirm({
      title,
      content,
      onOk,
      onCancel,
      okText,
      cancelText
    });
  },

  modalError,
  modelError: modalError, // 命名错误 保留兼容，使用modalError

  importXlsx: (fn: (value: any[]) => void) => {
    let input = document.createElement('input');
    input.type = 'file';
    input.accept = '.xlsx';
    input.onchange = (e: any) => {
      const files = e?.target?.files;
      const file = files?.[0];
      if (!files) return;
      const reader = new FileReader();
      reader.readAsBinaryString(file);
      reader.onload = async function (e) {
        let wb = XLSX.read(e?.target?.result, {
          type: 'binary'
        });
        let v: any[] = [];
        for (let index = 0; index < wb.SheetNames?.length; index++) {
          let _v: any[] = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[index]], { raw: false, dateNF: "yyyy-mm-dd" });
          _v = _v.map(item => ({ ...item, id: `$_${+(new Date())}` }));
          v.push(_v);
        }

        fn(v);
      };
    };
    input.click();
  },
  setSessionStorage: (k: string, data: any) => {
    window.sessionStorage.setItem(k, typeof data === 'string' ? data : JSON.stringify(data));
  },
  getSessionStorage: (k: string) => {
    const str = window.sessionStorage.getItem(k);
    if (str) {
      try {
        return JSON.parse(str);
      } catch (error) {
        return str;
      }
    }
  },
  removeSessionStorage: (k?: string) => {
    if (k) window.sessionStorage.removeItem(k);
    else window.sessionStorage.clear();
  },
  setLocalStorage: (k: string, data: any) => {
    window.localStorage.setItem(k, typeof data === 'string' ? data : JSON.stringify(data));
  },
  getLocalStorage: (k: string) => {
    const str = window.localStorage.getItem(k);
    if (str) {
      try {
        return JSON.parse(str);
      } catch (error) {
        return str;
      }
    }
  },
  removeLocalStorage: (k?: string) => {
    if (k) window.localStorage.removeItem(k);
    else window.localStorage.clear();
  },
  openPrint: (formName: string, sessionKey?: string, sessionData?: any) => {
    if (sessionKey && sessionData) {
      window.sessionStorage.setItem(sessionKey, JSON.stringify(sessionData));
    }

    window.open(`/${PACKAGE_NAME}/print?formName=${formName}&sessionkey=${sessionKey}`);
  },
  startWorkflow: async (options: OptionType, next: Function) => {
    const { formData, formId, workflowNumber, workflowCode, businessName } = options;

    if (workflowNumber) {
      console.warn('不推荐使用workflowNumber启动工作流，请使用workflowCode！');
    }

    if (next) {
      axios({
        url: '/bk/api/fd/FormData/add',
        method: 'POST',
        data: {
          data: {
            formId,
            data: JSON.stringify(formData)
          }
        }
      }).then(res => {
        if (res.status === 200 && res.data?.status === 200) {
          axios({
            url: '/bk/mid/workflow/createAndStartWorkflow',
            method: 'POST',
            data: {
              formId,
              workflowNumber,
              workflowCode,
              businessName,
              operateCode: '20',
              variables: formData,
              formDataId: res.data?.result?.id
            }
          })
            .then(res => next(res))
            .catch(error => {
              console.log(error);
            });
        } else {
          next(res);
        }
      }).catch(error => {
        console.log(error);
      });
    } else {
      const formDataRes = await units.serverLoading('/bk/api/fd/FormData/add', {
        data: {
          formId,
          data: JSON.stringify(formData)
        }
      }, {
        firstMessage: true,
        errorMessage: options.errorMessage
      });

      const res = await units.serverLoading('/bk/mid/workflow/createAndStartWorkflow', {
        formId,
        workflowNumber,
        workflowCode,
        businessName,
        operateCode: '20',
        variables: formData,
        formDataId: formDataRes.id
      }, {
        firstMessage: true,
        errorMessage: options.errorMessage
      });

      return res;
    }
  },
  flatObject: (obj: Object, flatObjectKeys: string[], isDeleteOriginalFlatObject = false) => {
    const tempObj = {};

    flatObjectKeys.forEach(objectKey => {
      const currObj = obj[objectKey] || {};

      for (let k in currObj) {
        tempObj[`${objectKey}.${k}`] = currObj[k];
      }

      if (isDeleteOriginalFlatObject) delete obj[objectKey];
    });

    Object.assign(obj, tempObj);
    return obj;
  },
  previewFile: (attachPath: string) => {
    const officeSuffix = ['pptx', 'ppt', 'doc', 'docx', 'xls', 'xlsx', 'pdf'];
    const imgSuffix = ['jpeg', 'gif', 'jpg', 'png', 'bmp'];
    const fileName = attachPath.split('?')?.[0];

    const suffix = fileName.split('.').pop()?.toLocaleLowerCase();
    if (suffix) {
      if (imgSuffix.includes(suffix)) {
        const newwindow = window.open(window.location.origin) as any;
        newwindow?.document?.write(`
          <html>
            <title>预览</title>
            <body style="display: flex; justify-content: center; align-items: center">
              <img src=${attachPath} />
            </body>
          </html>
        `);
      } else if (officeSuffix.includes(suffix)) {
        window.open('/bk/previewFile/' + attachPath.replace('/bk/getFile/', ''));
      } else {
        units.message.error('格式不支持');
      }
    }
  },
  downloadFiles: (files: string[]) => {
    files.forEach(file => {
      var iframe = document.createElement('iframe');
      iframe.style.display = 'none';
      document.body.appendChild(iframe);
      iframe.src = file;
    });
  },
  exportExcel
};

type EvalScriptText = {
  scriptText: string;
  next: Function;
  extra?: Record<string, any>; // 额外传入解析器参数
};

export function es6ToEs5(scriptText: string) {
  const output = (window as any).Babel.transform(`(async function(){${scriptText}})()`, { presets: ['env'] }).code;
  return output;
}

async function evalScriptText({ scriptText, extra, next }: EvalScriptText, transformed?: boolean) {
  const interpreter = new Interpreter({ ...ctx, ...extra, next });
  let output = scriptText;

  if (!transformed) output = es6ToEs5(scriptText);

  try {
    const res = await interpreter.evaluate(output);

    if (res && next) {
      if (Array.isArray(res)) {
        next(...res);
      } else {
        next(res);
      }
    }
  } catch (error) {
    console.log(error);
    next(error);
  }
}



export default evalScriptText;